/* $RCSFile: ModuleSettingsDictionary.cs $
 * Apache license v2 2011 by Christian Surieux
 * $Source: ?/ADNCRM.Core/Configuration/Settings/ModuleSettingsDictionary.cs $
 * $log$
 * Revision 6 2010/08/06 19:56:27  christian.surieux
 *   Added Log to All cs files
 */

using System;
using System.Diagnostics;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Collections.Generic;
using System.Web;
using System.Web.Security;
using System.Web.Caching;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Globalization;

using AddonNice.Diagnostics;
using AddonNice.UI;
using AddonNice.UI.WebControls;
using AddonNice.Settings;

using Path2     =   AddonNice.Settings.Path;
using Cache2    =   AddonNice.Settings.Cache.CurrentCache;
using Key2      =   AddonNice.Settings.Cache.Key;

namespace AddonNice.Configuration
{

    /// <summary>
    /// Dedicated dictionary for module settings
    /// </summary>
    public class ModuleSettingsDictionary : SettingsDictionary
    {

		#region This delegate is created to manage PagesSettings  Extensions added by pakages
		public static event ModuleSettingsCreatedEventHandler ModuleSettingsCreatedEvent;

		public void OnModuleSettingsCreated(ModuleSettingsCreatedEventArgs e) 
		{
			if ( ModuleSettingsCreatedEvent != null )
				ModuleSettingsCreatedEvent(e);
		}
		#endregion

        #region overrides from SettingsDictionary
        
        public override void AddUpper(string key, SettingItem value)
        {
            if ( value.Type == SettingsType.Unknown )
                value.Type  =    SettingsType.Module;
            Add(key.ToUpper(), value);
        }

        /// <summary>
        /// Uses the SettingItem.Key as key
        /// Force SettingItem.Type to Module if Unknown
        /// </summary>
        public override void AddSetting(SettingItem value)
        {
            if ( value.Type == SettingsType.Unknown )
                value.Type  =    SettingsType.Module;
            Add(value.Key, value);
        }

        #endregion overrides from SettingsDictionary

        /// <summary>
        /// The GetModuleSettings Method returns a SettingsDictionary of
        /// custom module specific settings from the database. This method is
        /// used by some user control modules to access misc settings.
        /// </summary>
        public static ModuleSettingsDictionary GetModuleSettings(int xmoduleID)
        {
			if( Cache2.Exists(Key2.ModuleSettings(xmoduleID)))
			{
			    if ( SettingsTraceSwitch.Sw.TraceVerbose )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleSettingsDictionary GetModuleSettings found in cache ModuleID: {0}",xmoduleID),SettingsTraceSwitch.Sw.Info);
                return (ModuleSettingsDictionary)Cache2.Get(Key2.ModuleSettings(xmoduleID));
			}
            return GetModuleSettings(xmoduleID, new ADNPage());
        }

        /// <summary>
        /// The GetModuleSettings Method returns a SettingsDictionary of
        /// custom module specific settings from the database. 
        /// Get the loading module path, loads the module, then loads custom setttings from DB
        /// </summary>
        public static ModuleSettingsDictionary GetModuleSettings(int ModuleID, ADNPage page)
        {
		    if ( SettingsTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleSettingsDictionary GetModuleSettings BEG ModuleID: {0}, PageID: {1} ",ModuleID,page.PageID),SettingsTraceSwitch.Sw.Info);
            PortalSettings portalSettings	            =	(PortalSettings) HttpContext.Current.Items[StStr.strPortalSettings];
            
            // 1-Initialize ModuleConfiguration for the module
            // look for an alredy loaded object in portalsettings
            ModuleConfiguration moduleConf              =   portalSettings.GetModuleConfiguration(ModuleID);
            ModulesDB mdb                               =   new ModulesDB();
            if ( moduleConf == null )
            {
                    moduleConf                          =   new ModuleConfiguration(ModuleID);
            }
            string ControlPath                          =   Path2.ApplicationRoot + "/"+moduleConf.DesktopSrc;
            PortalModuleControl portalModule            =   null;
            try
            {
                if (ModuleTraceSwitch.Sw.TraceVerbose)
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleSettingsDictionary GetModuleSettings LoadControl: {0}.", ControlPath), ModuleTraceSwitch.Sw.Info);
                // We just want to call the module constructor to instantiate settings, will not be added to page
                portalModule                            =   (PortalModuleControl)page.LoadControl(ControlPath);
                portalModule.ModuleBaseConfiguration    =   moduleConf;
            }
            catch (Exception ex)
            {
                if (SettingsTraceSwitch.Sw.TraceError)
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[E]ModuleSettingsDictionary GetModuleSettings ControlPath:{0}, ex: {1}", ControlPath, ex), SettingsTraceSwitch.Sw.Info);
                throw;
            }

            // only for pages which do not host modules, we store the module
            if (page is StdAuxPage)
            {
                page.LoadedModules.Add(ModuleID, portalModule);
	            if ( SettingsTraceSwitch.Sw.TraceVerbose )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleSettingsDictionary GetModuleSettings Add module to Page.LoadedModules Count: {0}.",page.LoadedModules.Count),SettingsTraceSwitch.Sw.Info);
            }


            // to allow calling Settings[] in a settingitem event handler
            portalModule.Settings                       =   portalModule.BaseSettings;
        	ModuleSettingsDictionary dic                =   portalModule.BaseSettings.LoadModuleSettings(ModuleID);
    	    if ( SettingsTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleSettingsDictionary GetModuleSettings Loaded ModuleID: {0}.",ModuleID),SettingsTraceSwitch.Sw.Info);
            if ( moduleConf.ModuleSettingsID == 0 )
            {
                if (SettingsTraceSwitch.Sw.TraceVerbose)
                    Trace.WriteLine("[V]ModuleSettingsDictionary GetModuleSettings END no merge.", SettingsTraceSwitch.Sw.Info);
                return dic;
            }

            // 2 - if necessary override or add module dedicated settings with these of the attached module
            dic.LoadAndMergeDic(moduleConf,page);
    	    if ( SettingsTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine("[V]ModuleSettingsDictionary GetModuleSettings END.",SettingsTraceSwitch.Sw.Info);
            return dic;
        }

        public void LoadAndMergeDic(ModuleConfiguration moduleConf,ADNPage page)
        {
            while ( moduleConf.ModuleSettingsID != 0 && 
                    (moduleConf.ModuleID != moduleConf.ModuleSettingsID) )
            {
                    moduleConf                                  =   new ModuleConfiguration(moduleConf.ModuleSettingsID);
            }
            string ControlPath                                  =   Path2.ApplicationRoot + "/"+moduleConf.DesktopSrc;
            PortalModuleControl portalModule2                   =   null;
            try
            {
                if (ModuleTraceSwitch.Sw.TraceVerbose)
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleSettingsDictionary LoadAndMergeDic LoadControl: {0}.", ControlPath), ModuleTraceSwitch.Sw.Info);
                // We just want to call the module constructor to instantiate settings, will not be added to any control in page 
                portalModule2                                   =   (PortalModuleControl)page.LoadControl(ControlPath);
                portalModule2.ModuleBaseConfiguration           =   moduleConf;
            }
            catch (Exception ex)
            {
                if (SettingsTraceSwitch.Sw.TraceError)
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[E]ModuleSettingsDictionary LoadAndMergeDic ControlPath:{0}, ex: {1}", ControlPath, ex), SettingsTraceSwitch.Sw.Info);
                throw;
            }
    	    if ( SettingsTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleSettingsDictionary LoadAndMergeDic END ModuleID: {0}.",moduleConf.ModuleID),SettingsTraceSwitch.Sw.Info);
        	// This is always same dic: portalModule2.BaseSettings
            ModuleSettingsDictionary dic2               =   portalModule2.BaseSettings.LoadModuleSettingsGroup(moduleConf.ModuleID,SettingItemGroup.MODULE_SPECIAL_SETTINGS);
        	portalModule2.BaseSettings.LoadModuleSettingsGroup(moduleConf.ModuleID,SettingItemGroup.MODULE_SPECIAL_SETTINGS1);
        	portalModule2.BaseSettings.LoadModuleSettingsGroup(moduleConf.ModuleID,SettingItemGroup.MODULE_SPECIAL_SETTINGS2);
            // Get the module special settings from the attached module, take all groups
            int cnt                                     =   Merge(dic2,SettingItemGroup.NONE,false);
    	    if ( SettingsTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleSettingsDictionary LoadAndMergeDic END Merged: {0}.",cnt),SettingsTraceSwitch.Sw.Info);
        }

            
        /// <summary>
        /// Returns a SettingsDictionary of module specific settings from the database.  
        /// Beware that _baseSettings must be conform to what is expected for module type identified by xModuleID
        /// key not presents in _baseSettings will be rejected
        /// </summary>
        public ModuleSettingsDictionary LoadModuleSettings(int xmoduleID) 
        {
            Status                          =   SettingsStatus.Loading;
		    if ( SettingsTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleSettingsDictionary LoadModuleSettings BEG count: {0}.",Count),SettingsTraceSwitch.Sw.Info);
            string cachekey                 =   Key2.ModuleSettings(xmoduleID);
			if( Cache2.Exists(cachekey))
			{
                ModuleSettingsDictionary  result    =   (ModuleSettingsDictionary )Cache2.Get(cachekey);
				if ( SettingsTraceSwitch.Sw.TraceVerbose )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleSettingsDictionary LoadModuleSettings Found in cache count: {0}",result.Count),SettingsTraceSwitch.Sw.Info);
                // we can't use result directly due to handlers set between settings in module ctor
                // so we assign correct value to the calling module settings and invert value in cache 
                List<SettingItem> ApplyLst      =   CreateDependencyList();
                for(int i=0; i < ApplyLst.Count ; i++ )
                {
                    SettingItem st          =   ApplyLst[i];
                    st.Type                 =   SettingsType.Module;
                    st.ParentDBId           =   xmoduleID;
                    if (result.ContainsKey(st.Key))
                    {
                        st.SetValueFromDB(result[st.Key].Value);
                        if (SettingsTraceSwitch.Sw.TraceVerbose)
                            Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleSettingsDictionary LoadModuleSettings key: {0} initialized.", st.Key), SettingsTraceSwitch.Sw.Info);
                    }
                }
                Status      =   SettingsStatus.Loaded;
                // swap objects in cache 
                Cache2.Remove(cachekey);
                CacheDependency dep     =   new CacheDependency(PortalSettings.webConfigPath);
                Cache2.Insert(cachekey, this,dep);
                return this;
			}
            ModulesDB mDB                   =   new ModulesDB();
			SqlDataReader dr				=	null;
			try
			{
			    dr				            =	mDB.GetModuleSettings(xmoduleID);
                ReadSettingsFromDB(dr);
                // Mark all settingsitems with type and moduleid
                foreach (KeyValuePair<string,SettingItem> pair in this)
                {
                    SettingItem st          =   pair.Value;
                    st.Type                 =   SettingsType.Module;
                    st.ParentDBId           =   xmoduleID;
				    if ( SettingsTraceSwitch.Sw.TraceVerbose )
                        Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleSettingsDictionary LoadModuleSettings key: {0}, xmoduleID: {1}",pair.Key,xmoduleID),SettingsTraceSwitch.Sw.Info);
                }

            }
			catch(Exception ex )
			{
				if ( SettingsTraceSwitch.Sw.TraceError )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[E]ModuleSettingsDictionary LoadModuleSettings ex : {0}",ex),SettingsTraceSwitch.Sw.Info);
				throw;
			}
			finally
			{
				if ( dr != null )
					dr.Close();
			}
            Status      =   SettingsStatus.Loaded;
            if ( Count > 0)
            {
                CacheDependency dep     =   new CacheDependency(PortalSettings.webConfigPath);
                Cache2.Insert(cachekey, this,dep);
            }
		    if ( SettingsTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleSettingsDictionary LoadModuleSettings END count: {0}.",Count),SettingsTraceSwitch.Sw.Info);
            return this;
        }

        public ModuleSettingsDictionary LoadModuleSettingsGroup(int xmoduleID,SettingItemGroup Group) 
        {
		    if ( SettingsTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleSettingsDictionary LoadModuleSettingsGroup BEG count: {0}, Group : {1}.",Count,Group),SettingsTraceSwitch.Sw.Info);
            ModulesDB mDB                   =   new ModulesDB();
			SqlDataReader dr				=	null;
			try
			{
			    dr				            =	mDB.GetModuleSettingsGroup(xmoduleID,Group);
                ReadSettingsFromDB(dr);
                // Mark all settingsitems with type and moduleid
                foreach (KeyValuePair<string,SettingItem> pair in this)
                {
                    SettingItem st          =   pair.Value;
                    if ( st.Group != Group )
                        continue;
                    st.Type                 =   SettingsType.Module;
                    st.ParentDBId           =   xmoduleID;
				    if ( SettingsTraceSwitch.Sw.TraceVerbose )
                        Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleSettingsDictionary LoadModuleSettingsGroup key: {0}, xmoduleID: {1}",pair.Key,xmoduleID),SettingsTraceSwitch.Sw.Info);
                }
            }
			catch(Exception ex )
			{
				if ( SettingsTraceSwitch.Sw.TraceError )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[E]ModuleSettingsDictionary LoadModuleSettingsGroup ex : {0}",ex),SettingsTraceSwitch.Sw.Info);
				throw;
			}
			finally
			{
				if ( dr != null )
					dr.Close();
			}
		    if ( SettingsTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleSettingsDictionary LoadModuleSettingsGroup END count: {0}.",Count),SettingsTraceSwitch.Sw.Info);
            return this;
        }
        
        /// <summary>
        /// Copy values with the specified group from dic 
        /// New settingsitems values contain their original type and moduleid
        /// </summary>
        public int Merge(ModuleSettingsDictionary dic, SettingItemGroup Group,bool useGroup)
        {
            if (SettingsTraceSwitch.Sw.TraceVerbose)
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleSettingsDictionary Merge BEG Group: {0}, useGroup: {1}.", Group,useGroup), SettingsTraceSwitch.Sw.Info);
            int cnt             =   0;
            foreach (KeyValuePair<string, SettingItem> pair in dic)
            {
                SettingItem st =    pair.Value;
                if ( useGroup && st.Group != Group )
                    continue;
                if (SettingsTraceSwitch.Sw.TraceVerbose)
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleSettingsDictionary Merge found Key: '{0}', Value: '{1}'", st.Key, st.Value), SettingsTraceSwitch.Sw.Info);
                // Only override authorized settings
                if (ContainsKey(pair.Key) && !this[pair.Key].CouldOverride)
                {
                    if (SettingsTraceSwitch.Sw.TraceVerbose)
                        Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleSettingsDictionary Merge Couldn't override Key: '{0}'.", st.Key), SettingsTraceSwitch.Sw.Info);
                    continue;
                }
                // preserve the original group and order when working on whole dictionary (else could create duplicates)
                if (!useGroup && ContainsKey(pair.Key))
                {
                    SettingItem oldst   =   this[pair.Key];
                    st.Group            =   oldst.Group;
                    st.Order            =   oldst.Order;
                }
                this[pair.Key]    =   st;
                cnt++;
            }
            if (SettingsTraceSwitch.Sw.TraceVerbose)
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleSettingsDictionary Merge END Cnt: {0}.", cnt), SettingsTraceSwitch.Sw.Info);
            return cnt;
            }

        public void ReadSettingsFromDB(SqlDataReader dr) 
        {
            int cnt             =   0;
            Dictionary<string,string> valDic    =   new Dictionary<string,string>();
			while (dr.Read()) 
			{
                cnt++;
                string key      =   (string)dr[StStr.DBSettingName];
                // empty or null keys not allowed
                if (string.IsNullOrEmpty(key))
                    continue;
                // nul content not allowed but string.empty allowed
                object ob       =   dr[StStr.DBSettingValue];
                if ( ob == DBNull.Value )
                    continue;
                int moduleID    =   (int)dr[StStr.DBModuleID];
                //int group       =   (int)dr[StStr.DBSettingGroup];
                string val      =   ob.ToString();
                string k        =   key.ToUpper();
                bool ok         =   ContainsKey(k);
                if (ok)
                {
                    SettingItem it          =   this[k];
                    if (!val.Equals(it.Value))
                    {
                        if (SettingsTraceSwitch.Sw.TraceVerbose)
                            Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleSettingsDictionary ReadSettingsFromDB key: {0}, val: {1}, old val:{2}",
                                new object[] { k, val, it.Value }), SettingsTraceSwitch.Sw.Info);
                        it.ParentDBId       =   moduleID;
                        it.Key              =   k;
                        valDic.Add(k,val);
                    }
                if ( !ok && SettingsTraceSwitch.Sw.TraceVerbose )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleSettingsDictionary ReadSettingsFromDB KEY FOUND IN DB BUT NOT IN baseSettings key: {0}, val: {1}",
                                k, val), SettingsTraceSwitch.Sw.Info);

                }
			}
            // if any, apply changes in order: items with dependencies fisrt
            if ( valDic.Count > 0)
            {
                List<SettingItem> ApplyLst      =   CreateDependencyList();
                for (int i = 0; i < ApplyLst.Count; i++)
                {
                    SettingItem st = ApplyLst[i];
                    if (valDic.ContainsKey(st.Key))
                    {
                        st.SetValueFromDB(valDic[st.Key]);
                    }
                }
            }
			if ( SettingsTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleSettingsDictionary ReadSettingsFromDB read from db cnt: {0}, changed: {1}",cnt,valDic.Count ),SettingsTraceSwitch.Sw.Info);
		}

		/// <summary>
        /// The GetModuleSettings Method returns a SettingsDictionary of
		/// custom module specific settings from the database. This method is
		/// used by some user control modules to access misc settings.
		/// </summary>
        public static ModuleSettingsDictionary GetModuleUserSettings(int moduleID, Guid userID, Page page)
		{
			string ControlPath  =   Path.ApplicationRoot + "/";
            SqlDataReader dr    =   null;
            try
            {
                dr              =   new ModulesDB().GetModuleConfigurationForModule(moduleID);
                if (dr.Read())
                    ControlPath +=  dr[StStr.DBDesktopSrc].ToString();
            }
            catch (Exception ex)
            {
                if ( GlobalTraceSwitch.Sw.TraceError )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[E]ModuleSettingsDictionary GetModuleUserSettings moduleID: {0}, ex: {1}", moduleID, ex),GlobalTraceSwitch.Sw.Info);
                throw;
            }
            finally
            {
                if (dr != null && !dr.IsClosed)
                    dr.Close();
            }

            PortalModuleControlCustom portalModule  = null;
            try
            {
                if ( ModuleTraceSwitch.Sw.TraceVerbose )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]ModuleSettingsDictionary GetModuleUserSettings LoadControl : {0}.", ControlPath),ModuleTraceSwitch.Sw.Info);
                portalModule = (PortalModuleControlCustom)page.LoadControl(ControlPath);
            }
            catch (Exception ex)
            {
                if ( SettingsTraceSwitch.Sw.TraceError )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[E]ModuleSettingsDictionary GetModuleUserSettings ControlPath: {1}, ex: {1}", ControlPath, ex),SettingsTraceSwitch.Sw.Info);
                throw new Exception("ModuleConfigurationCustom GetModuleUserSettings there was a problem loading: '" + ControlPath + "'", ex);
            }
            return portalModule.CustomizedUserSettings.LoadModuleUserSettings(moduleID,PortalSettings.CurrentIdentity.CrmId);
		}

        /// <summary>
		/// Retrieves the custom user settings for the current user for this module
		/// from the database.
		/// </summary>
        public ModuleSettingsDictionary LoadModuleUserSettings(int moduleID, Guid userID) 
		{			
            ModulesDB mDB                   =   new ModulesDB();
			SqlDataReader dr				=	null;
			try
			{
			    dr				            =	mDB.GetModuleUserSettings(moduleID,userID);
                ReadSettingsFromDB(dr);
            }
            catch (Exception ex)
            {
                if ( GlobalTraceSwitch.Sw.TraceError )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[E]ModuleSettingsDictionary LoadModuleUserSettings moduleID: {0}, ex: {1}", moduleID,ex),GlobalTraceSwitch.Sw.Info);
                throw;
            }
            finally
            {
                if (dr != null && !dr.IsClosed)
                    dr.Close();
            }
			return this;
		}

    }
}
